direct_url_helpers.py 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130
  1. import logging
  2. from pip._internal.models.direct_url import (
  3. DIRECT_URL_METADATA_NAME,
  4. ArchiveInfo,
  5. DirectUrl,
  6. DirectUrlValidationError,
  7. DirInfo,
  8. VcsInfo,
  9. )
  10. from pip._internal.utils.typing import MYPY_CHECK_RUNNING
  11. from pip._internal.vcs import vcs
  12. try:
  13. from json import JSONDecodeError
  14. except ImportError:
  15. # PY2
  16. JSONDecodeError = ValueError # type: ignore
  17. if MYPY_CHECK_RUNNING:
  18. from typing import Optional
  19. from pip._internal.models.link import Link
  20. from pip._vendor.pkg_resources import Distribution
  21. logger = logging.getLogger(__name__)
  22. def direct_url_as_pep440_direct_reference(direct_url, name):
  23. # type: (DirectUrl, str) -> str
  24. """Convert a DirectUrl to a pip requirement string."""
  25. direct_url.validate() # if invalid, this is a pip bug
  26. requirement = name + " @ "
  27. fragments = []
  28. if isinstance(direct_url.info, VcsInfo):
  29. requirement += "{}+{}@{}".format(
  30. direct_url.info.vcs, direct_url.url, direct_url.info.commit_id
  31. )
  32. elif isinstance(direct_url.info, ArchiveInfo):
  33. requirement += direct_url.url
  34. if direct_url.info.hash:
  35. fragments.append(direct_url.info.hash)
  36. else:
  37. assert isinstance(direct_url.info, DirInfo)
  38. # pip should never reach this point for editables, since
  39. # pip freeze inspects the editable project location to produce
  40. # the requirement string
  41. assert not direct_url.info.editable
  42. requirement += direct_url.url
  43. if direct_url.subdirectory:
  44. fragments.append("subdirectory=" + direct_url.subdirectory)
  45. if fragments:
  46. requirement += "#" + "&".join(fragments)
  47. return requirement
  48. def direct_url_from_link(link, source_dir=None, link_is_in_wheel_cache=False):
  49. # type: (Link, Optional[str], bool) -> DirectUrl
  50. if link.is_vcs:
  51. vcs_backend = vcs.get_backend_for_scheme(link.scheme)
  52. assert vcs_backend
  53. url, requested_revision, _ = (
  54. vcs_backend.get_url_rev_and_auth(link.url_without_fragment)
  55. )
  56. # For VCS links, we need to find out and add commit_id.
  57. if link_is_in_wheel_cache:
  58. # If the requested VCS link corresponds to a cached
  59. # wheel, it means the requested revision was an
  60. # immutable commit hash, otherwise it would not have
  61. # been cached. In that case we don't have a source_dir
  62. # with the VCS checkout.
  63. assert requested_revision
  64. commit_id = requested_revision
  65. else:
  66. # If the wheel was not in cache, it means we have
  67. # had to checkout from VCS to build and we have a source_dir
  68. # which we can inspect to find out the commit id.
  69. assert source_dir
  70. commit_id = vcs_backend.get_revision(source_dir)
  71. return DirectUrl(
  72. url=url,
  73. info=VcsInfo(
  74. vcs=vcs_backend.name,
  75. commit_id=commit_id,
  76. requested_revision=requested_revision,
  77. ),
  78. subdirectory=link.subdirectory_fragment,
  79. )
  80. elif link.is_existing_dir():
  81. return DirectUrl(
  82. url=link.url_without_fragment,
  83. info=DirInfo(),
  84. subdirectory=link.subdirectory_fragment,
  85. )
  86. else:
  87. hash = None
  88. hash_name = link.hash_name
  89. if hash_name:
  90. hash = "{}={}".format(hash_name, link.hash)
  91. return DirectUrl(
  92. url=link.url_without_fragment,
  93. info=ArchiveInfo(hash=hash),
  94. subdirectory=link.subdirectory_fragment,
  95. )
  96. def dist_get_direct_url(dist):
  97. # type: (Distribution) -> Optional[DirectUrl]
  98. """Obtain a DirectUrl from a pkg_resource.Distribution.
  99. Returns None if the distribution has no `direct_url.json` metadata,
  100. or if `direct_url.json` is invalid.
  101. """
  102. if not dist.has_metadata(DIRECT_URL_METADATA_NAME):
  103. return None
  104. try:
  105. return DirectUrl.from_json(dist.get_metadata(DIRECT_URL_METADATA_NAME))
  106. except (
  107. DirectUrlValidationError,
  108. JSONDecodeError,
  109. UnicodeDecodeError
  110. ) as e:
  111. logger.warning(
  112. "Error parsing %s for %s: %s",
  113. DIRECT_URL_METADATA_NAME,
  114. dist.project_name,
  115. e,
  116. )
  117. return None